package com.lagou.edu.spring.core.defaults;

import com.lagou.edu.spring.core.BeanDefinition;
import com.lagou.edu.spring.core.BeanDefinitionRegistry;
import com.lagou.edu.spring.core.BeanFactory;
import com.lagou.edu.spring.core.BeanFactoryPostProcessor;
import com.lagou.edu.spring.core.BeanPostProcessor;
import com.lagou.edu.spring.core.ObjectFactory;
import com.lagou.edu.spring.exceptions.MySpringException;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * bean工厂实现类
 *
 * @author 大犬
 * @date 2020/3/31
 */
public class DefaultBeanFactory implements BeanFactory {

    private Map<String, Object> objectMap;

    private BeanInitializer beanInitializer;

    private BeanInstantiationFactory beanInstantiationFactory;

    private EarlyBeanFactory earlyBeanFactory;

    private boolean finished;

    private BeanDefinitionRegistry beanDefinitionRegistry;

    public void importBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) {
        this.beanDefinitionRegistry = beanDefinitionRegistry;
        objectMap = new HashMap<>(10);
        earlyBeanFactory = new EarlyBeanFactory();
        beanInitializer = new BeanInitializer(this);
        beanInstantiationFactory = new BeanInstantiationFactory(this);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getBean(String beanName, Class<T> beanType) {
        return (T) getBean(beanName);
    }


    @Override
    @SuppressWarnings("unchecked")
    public <T> List<T> getBeans(Class<T> beanType) {
        if (finished) {
            List<Object> beanList = objectMap.values()
                    .stream()
                    .filter(o -> beanType.isAssignableFrom(o.getClass()))
                    .collect(Collectors.toList());
            if (beanList.size() == 0) {
                throw new MySpringException(MessageFormat.format("no bean with type({0}) in factory", beanType.getName()));
            }
            return (List<T>) beanList;
        }
        List<BeanDefinition> beanDefinitions = beanDefinitionRegistry.getBeanDefinitions(beanType);
        // 判断是否有该bean的定义信息
        if (beanDefinitions == null) {
            throw new MySpringException(MessageFormat.format("no bean with type({0}) in factory", beanType.getName()));
        }
        if (beanDefinitions.size() == 0) {
            throw new MySpringException(MessageFormat.format("no bean with type({0}) in factory", beanType.getName()));
        }
        return (List<T>) beanDefinitions.stream()
                .map(BeanDefinition::getBeanName)
                .map(this::getBean)
                .collect(Collectors.toList());
    }

    @Override
    public Object getBean(String beanName) {
        // 1.通过名称先从map中获取
        Object bean = objectMap.get(beanName);
        // 2.如果有则直接返回
        if (bean != null) {
            return bean;
        }
        // 3.如果没有，则进行创建bean逻辑,并返回创建好的bean
        return doCreateBean(beanName);
    }


    @Override
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> beanType) {
        List<T> beans = getBeans(beanType);
        if (beans.size() != 1) {
            throw new MySpringException(MessageFormat.format("no bean with type({0}) in factory", beanType.getName()));
        }
        return beans.get(0);
    }

    /**
     * 创建bean
     *
     * @param beanName bean名称
     * @return 返回创建好的bean
     */
    private Object doCreateBean(String beanName) {
        // 从bean的定义信息注册中心获取bean的定义信息
        BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanName);
        if (beanDefinition == null) {
            throw new MySpringException(MessageFormat.format("no bean with name({0}) in factory",
                    beanName));
        }
        // 获取早期bean
        Object earlyBean = getEarlyBean(beanName, true);
        Object originBean;
        if (earlyBean != null) {
            return earlyBean;
        } else {
            // 根据bean的定义信息创建bean
            originBean = instantBean(beanDefinition);
            // 提前暴露原始bean
            earlyExposeOriginBean(beanName, originBean);
            // 依赖处理
            populate(originBean, beanDefinition);
        }
        // 初始化之前获取早期bean，但是如果获取到的是ObjectFactory，则直接返回空
        earlyBean = getEarlyBean(beanName, false);
        // 进行bean的初始化操作（前置处理，初始化方法调用，后置处理）
        Object beanWrapper = initBean(originBean, beanDefinition);
        // 如果早期bean和初始化后的bean不同，则需要报循环依赖的错误
        // 因为这种情况的产生，意味着某个bean的属性设置了一个与当前容器中不一样的bean，这种情况是很危险的
        if (earlyBean != null && beanWrapper != earlyBean) {
            throw new MySpringException("circular dependency!");
        }
        // 将创建好的bean放入容器中
        addBean(beanName, beanWrapper);
        // 清理早期bean
        clearEarlyBean(beanName);
        return beanWrapper;
    }

    private void clearEarlyBean(String beanName) {
        earlyBeanFactory.deleteEarlyBean(beanName);
    }

    private void earlyExposeOriginBean(String beanName, Object originBean) {
        earlyBeanFactory.addOriginBean(beanName, originBean);
        earlyBeanFactory.addEarlyBean(beanName,
                (ObjectFactory) () -> beanInitializer.earlyWrapper(beanName, originBean)
        );
    }

    private Object getEarlyBean(String beanName, boolean exposeEarlyBean) {
        return earlyBeanFactory.getEarlyBean(beanName, exposeEarlyBean);
    }

    private void populate(Object originBean, BeanDefinition beanDefinition) {
        beanInstantiationFactory.populate(originBean, beanDefinition);
    }


    /**
     * 初始化bean，在这个方法中主要完成以下工作：
     * 依赖处理,前置处理，初始化方法调用，后置处理
     *
     * @param originBean
     * @return
     */
    private Object initBean(Object originBean, BeanDefinition beanDefinition) {
        String beanName = beanDefinition.getBeanName();
        Object bean = originBean;
        boolean wrapperNecessary = true;
        if (BeanPostProcessor.class.isAssignableFrom(beanDefinition.getBeanType())) {
            wrapperNecessary = false;
        }
        if (BeanFactoryPostProcessor.class.isAssignableFrom(beanDefinition.getBeanType())) {
            wrapperNecessary = false;
        }
        if (beanDefinition.getSkipWrapper() != null && beanDefinition.getSkipWrapper()) {
            wrapperNecessary = false;
        }
        // 前置处理
        if (wrapperNecessary) {
            bean = beanInitializer.preProcess(beanName, bean);
        }

        // 初始化
        beanInitializer.initBean(bean, beanDefinition);

        // 后置处理
        if (wrapperNecessary) {

            bean = beanInitializer.afterProcess(beanName, bean);
        }
        return bean;
    }

    /**
     * 实例化bean
     *
     * @param beanDefinition bean定义信息
     * @return 返回实例化的bean对象
     */
    private Object instantBean(BeanDefinition beanDefinition) {
        return beanInstantiationFactory.instantiatesBean(beanDefinition);
    }


    private void addBean(String beanName, Object bean) {
        if (objectMap.containsKey(beanName)) {
            throw new MySpringException(MessageFormat.format("{0} is already exist in factory", beanName));
        }
        objectMap.put(beanName, bean);
    }

    public void finish() {
        finished = true;
        earlyBeanFactory.clear();
        beanDefinitionRegistry.clear();
    }

    public void clear() {
        objectMap.clear();
    }


}
